<?php

namespace Kangcg\Application\Security;

/**
 * 数据签名包
 * Class SignData
 * @package Kangcg\Helper\Security
 */
class Signature
{
    public function __construct(SecurityInterface $security)
    {
        $this->_security = $security;
    }

    public function encode($item)
    {
        $data['encryption'] = $this->_security->getEncryption();
        $data['pack'] = $this->_security->encode($item);
        $data['timestamp'] = time();
        $data['sign'] = $this->getSign($data);

        return $data;
    }

    /**
     * @param array $data ['encryption' => '签名名称', 'pack' => '签名数据', 'timestamp' => '签名时间戳', 'sign' => '签名']
     * @param bool $isCheck
     * @return mixed
     * @throws \Exception
     */
    public function decode(array $data, $isCheck = true, int $ttl = 120)
    {
        if ($isCheck) {
            if ($data['encryption'] != $this->_security->getEncryption()) {
                throw new \Exception('签名工具包错误！');
            }

            if (time() - $data['timestamp'] > $ttl) {
                throw new \Exception('数据已过去！');
            }

            $sign = $data['sign'];
            unset($data['sign']);
            if ($sign != $this->getSign($data)) {
                throw new \Exception('数据验签失败！');
            }
        }

        return $this->decodePack($data['pack']);
    }

    /**
     * @param $pack
     * @return mixed
     * @throws \Exception
     */
    public function decodePack($pack)
    {
        if (!$data = $this->_security->decode($pack)) {
            throw new \Exception('数据解密失败！');
        }

        return $data;
    }

    public function getSign(array $data)
    {
        $data['key'] = $this->_security->getConfig('key');
        ksort($data);
        $string = '';
        foreach ($data as $key => $value) {
            $string .= ($key . '=' . $value) . '&';
        }

        $string = trim($string, '&');
        return $this->_security->sign($string);
    }

    public static function getInstall(SecurityInterface $security)
    {
        if (self::$_install == null) {
            self::$_install = new static($security);
        }

        return self::$_install;
    }

    private static $_install = null;
    private SecurityInterface $_security;
}
